/** @file shader.h
    @brief Shaders */

#ifndef G3_SHADER_H
#define G3_SHADER_H

#include "log.h"
#include "types.h"
#include "text.h"
#include "file/f_absfs.h"
#include "file/f_fstream.h"
#include "gui/gui_typemgr.h"
#include "res_texture_mgr.h"
#include <string>
#include <vector>

namespace G3 {

/** Shader profiles */
enum SHADER_PROFILE {
    /** Vertex shader */
    SP_VERTEX = 0,
    /** Fragment shader */
    SP_FRAGMENT = 1,
    /** Geometry shader */
    SP_GEOMETRY = 2
};

/** Shader formats */
enum SHADER_FORMAT {
    /** Unknown format - not executed */
    SF_UNKNOWN = 0,
    /** A precompiled ARB assembler */
    SF_ASM = 1,
    /** ARB assembler source */
    SF_ASM_ASCII = 2,
    /** OpenGL slang */
    SF_GLSL = 3
};

/** @class SHADER_BASE
    @brief A base class for all shader profiles */
class SHADER_BASE: public RESOURCE_BASE {
    public:
        /**
            @brief A Constructor
        */
        SHADER_BASE ();
        /**
            @brief Empty virtual destructor
        */
        virtual ~SHADER_BASE () {
			// __asm ("nop");
			//		aka Do Nothing
        }

        /** Shader program index */
        uint programID;
        /** The shader format \see SHADER_FORMAT */
        SHADER_FORMAT shaderFormat;
        /** Filename of the shader */
        std::string filename;
        /** Is this shader valid for execution ? */
        bool valid;

        /** Pointer to the log */
        G3::LOG *Log;

        /**
            @brief Gets the fileformat by extension
            @param[in] Filename Reference to the filename
            @return The shader format \see SHADER_FORMAT
        */
        SHADER_FORMAT GetFileFormat (const std::string &Filename);

        /**
            @brief Loads the shader
            @param[in] Filename Reference to the shader file to be loaded
            @return True on success, false on failure
        */
        virtual bool Load (const std::string &Filename) { Log->Report ("SHADER_BASE::Load: Virtual call\n"); return true; }

        /**
            @brief Applies the shader
        */
        virtual void Apply () {}
        /**
            @brief Unapplies the shader
        */
        virtual void UnApply () {}

        /**
            @brief Sets an uniform sampler2D
            @param[in] program_id The master shader program index \see SHADER_PROGRAM
            @param[in] name Reference to the uniform variable name
            @param[in] handle Handle of the texture to be assigned to the variable
            @return True on success, false on failure
            @note SHADER_PROGRAM::Apply should be called before setting any uniform variables
        */
        bool SetTexture2D (uint program_id, const std::string &name, H_TEXTURE handle);

        /**
            @brief Sets an uniform float
            @param[in] program_id The master shader program index \see SHADER_PROGRAM
            @param[in] name Reference to the uniform variable name
            @param[in] value The value to be assigned
            @return True on success, false on failure
            @note SHADER_PROGRAM::Apply should be called before setting any uniform variables
        */
        bool SetFloat (uint program_id, const std::string &name, float value);

        /**
            @brief Sets an uniform vec3
            @param[in] program_id The master shader program index \see SHADER_PROGRAM
            @param[in] name Reference to the uniform variable name
            @param[in] value Reference to the value to be assigned
            @return True on success, false on failure
            @note SHADER_PROGRAM::Apply should be called before setting any uniform variables
        */
        bool SetVec3 (uint program_id, const std::string &name, const VEC3F &value);

        /**
            @brief Sets an uniform vec4
            @param[in] program_id The master shader program index \see SHADER_PROGRAM
            @param[in] name Reference to the uniform variable name
            @param[in] value Reference to the value to be assigned
            @return True on success, false on failure
            @note SHADER_PROGRAM::Apply should be called before setting any uniform variables
        */
        bool SetVec4 (uint program_id, const std::string &name, const COL4F &value);

        /**
            @brief Returns the type of this shader profile
            @return Type index for SP_DUMMY
        */
        virtual uint GetType () { return MainTypeMgr.GetType ("SP_DUMMY"); }

        /**
            @brief Checks if the shader is supported
            @param[in] Filename Reference to the shader filename to be checked
            @return True if it is and false, if it is not
        */
        virtual bool IsSupported (const std::string &Filename) { Log->Report ("SHADER_BASE::IsSupported: Virtual call\n"); return true; }

        /**
            @brief Releases the shader from the card
            \todo To be done
        */
        void Release () {}

        /**
            @brief Reloads the shader
        */
        bool Recreate () {
            return Load (filename);
        }

        /**
            @brief Gets the size of the shader in memory
            @return The size in bytes
        */
        long GetSize () {
            return filename.size () + sizeof (bool) + sizeof (G3::LOG *) + sizeof (SHADER_FORMAT) + sizeof (uint);
        }

        /**
            @brief Clears the shader
        */
        void Clear ();

    protected:
        /** The number of used image units */
        int numImageUnits;
        /** A map of image unit indexes and uniform sampler names */
        std::map<std::string, int> samplerImageUnits;
};

/** @class VERTEX_SHADER
    @brief A vertex shader object */
class VERTEX_SHADER : public SHADER_BASE {
    public:
        /**
            @brief A constructor
        */
        VERTEX_SHADER ();
        /**
            @brief A destructor
        */
        ~VERTEX_SHADER ();

        /**
            @brief Loads the vertex shader
            @param[in] Filename Reference to the name of the file to be loaded
            @return True on success, false on failure
        */
        bool Load (const std::string &Filename);

        /**
            @brief Applies the vertex shader
        */
        void Apply ();
        /**
            @brief Unapplies the vertex shader
        */
        void UnApply ();

        /**
            @brief Gets the type of this shader object
            @return Type index for SP_VERTEX
        */
        virtual uint GetType () { return MainTypeMgr.GetType ("SP_VERTEX"); }

        /**
            @brief Checks if the given vertex shader is supported
            @param[in] Filename Reference to the filename to be checked
            @return True if it is, otherwise false
        */
        bool IsSupported (const std::string &Filename);
};

/** @class FRAGMENT_SHADER
    @brief A fragment shader object */
class FRAGMENT_SHADER : public SHADER_BASE {
    public:
        /**
            @brief A constructor
        */
        FRAGMENT_SHADER ();
        /**
            @brief A destructor
        */
        ~FRAGMENT_SHADER ();

        /**
            @brief Loads the fragment shader
            @param[in] Filename Reference to the name of the file to be loaded
            @return True on success, false on failure
        */
        bool Load (const std::string &Filename);

        /**
            @brief Applies the fragment shader
        */
        void Apply ();
        /**
            @brief Unapplies the fragment shader
        */
        void UnApply ();

        /**
            @brief Gets the type of this shader object
            @return Type index for SP_FRAGMENT
        */
        virtual uint GetType () { return MainTypeMgr.GetType ("SP_FRAGMENT"); }

        /**
            @brief Checks if the given fragment shader is supported
            @param[in] Filename Reference to the filename to be checked
            @return True if it is, otherwise false
        */
        bool IsSupported (const std::string &Filename);
};

typedef G3::HANDLE<G3::SHADER_BASE> H_SHADER;
typedef G3::HANDLE<G3::VERTEX_SHADER> H_VSHADER;
typedef G3::HANDLE<G3::FRAGMENT_SHADER> H_PSHADER;

}

#endif // G3_SHADER_H
